using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Net;
using System.Xml;
using System.Diagnostics;
using System.Collections.Specialized;
using System.Threading;
using System.Drawing;


namespace PocketLibrary
{
    public class XMLRPCCommunication
    {
        public class ReceiveEventArgs : EventArgs
        {
            private string _received;

            public string ReceivedString
            {
                get { return _received; }
            }

            public XmlDocument ReceivedXml
            {
                get
                {
                    XmlDocument retval = new XmlDocument();
                    retval.LoadXml(_received);
                    return retval;
                }
            }

            private int _status;

            public int Status
            {
                get { return _status; }
                set { _status = value; }
            }


            public ReceiveEventArgs(string received, int status)
            {
                _received = received;
                Status = status;
            }
        }

        public delegate void ReceivedHandler(object sender, ReceiveEventArgs e);

        public event UploadProgressHandler OnUploadProgress;
        public event UploadProgressHandler OnDownloadProgress;
        public event ReceivedHandler OnReceived;

        private Dictionary<string,string> _responseHeader=new Dictionary<string,string>();
        
        public Dictionary<string,string> ResponseHeaders
        {
            get { return _responseHeader; }
        }


        private string _requestString = "";

        public string RequestString
        {
            get { return _requestString; }
            set { _requestString = value; }
        }

        private string _methodName="";

        public string MethodName
        {
            get { return _methodName; }
            set { _methodName = value; }
        }

        private List<XMLRPCParameter> _params=new List<XMLRPCParameter>();

        public List<XMLRPCParameter> Params
        {
            get { return _params; }
            set { _params = value; }
        }


        private string _url = "";

        public string Url
        {
            get { return _url; }
            set { _url = value; }
        }

        private string _userAgent = "TaskuBlogi 1.0";

        public string UserAgent
        {
            get { return _userAgent; }
            set { _userAgent = value; }
        }

        private string _contentType = "text/xml";

        public string ContentType
        {
            get { return _contentType; }
            set { _contentType = value; }
        }

        private string _uploadString = "";

        public string UploadString
        {
            get { return _uploadString; }
            set { _uploadString = value; }
        }

        private bool _useStringBuilder = false;

        public bool UseStringBuilder
        {
            get { return _useStringBuilder; }
            set { _useStringBuilder = value; }
        }

        private StringBuilder _uploadStringBuilder = new StringBuilder();

        public StringBuilder UploadStringBuilder
        {
            get { return _uploadStringBuilder; }
            set { _uploadStringBuilder = value; }
        }

        private string _response = "";

        public string Response
        {
            get { return _response; }
            //set { _response = value; }
        }


        public XMLRPCCommunication(string url)
        {
            Url = url;
        }

        public void SendReceive()
        {
            StringBuilder receivingStringBuilder = new StringBuilder();
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(Url);

            req.Method = "POST";
            req.UserAgent = UserAgent;
            req.ContentType = ContentType;

            byte[] data;
            if (_params.Count > 0 || _methodName != "")
            {
                // do work
                // clear list
                this._uploadStringBuilder = new StringBuilder();

                //this._uploadStringBuilder.Append();
                this._uploadStringBuilder.Append("<?xml version=\"1.0\" encoding=\"utf-8\"?>"); // encoding=\"iso-8859-1\"
                this._uploadStringBuilder.Append("<methodCall>");
                this._uploadStringBuilder.Append("<methodName>");
                this._uploadStringBuilder.Append(this.MethodName);
                this._uploadStringBuilder.Append("</methodName>");
                this._uploadStringBuilder.Append("<params>");

                foreach (XMLRPCParameter arg in Params)
                {
                    switch (arg.Type)
                    {
                        case XMLRPCDataType.NotSet:
                            this._uploadStringBuilder.Append("<param><value>" + arg.Value + "</value></param>");
                            break;
                        case XMLRPCDataType.String:
                            this._uploadStringBuilder.Append("<param><value><string>" + arg.Value + "</string></value></param>");
                            break;
                        case XMLRPCDataType.Int:
                            this._uploadStringBuilder.Append("<param><value><int>" + arg.Value + "</int></value></param>");
                            break;
                        case XMLRPCDataType.Float:
                            this._uploadStringBuilder.Append("<param><value><float>" + arg.Value + "</float></value></param>");
                            break;
                        case XMLRPCDataType.Boolean:
                            this._uploadStringBuilder.Append("<param><value><bool>" + arg.Value + "</bool></value></param>");
                            break;
                        case XMLRPCDataType.CData:
                            this._uploadStringBuilder.Append("<param><value><![CDATA[" + arg.Value + "]]></value></param>");
                            break;
                        default:
                            break;
                    }
                }

                this._uploadStringBuilder.Append("</params>");
                this._uploadStringBuilder.Append("</methodCall>");

                data = Encoding.ASCII.GetBytes(this._uploadStringBuilder.ToString());


            }
            else if (UseStringBuilder)
            {
                data = Encoding.ASCII.GetBytes(UploadStringBuilder.ToString());
            }
            else
            {
                data = Encoding.ASCII.GetBytes(UploadString);
            }
            req.ContentLength = data.Length;

            #region Make request
            int j = 1;
            int uploadBit = Math.Max(data.Length / 100, 50 * 1024);
            int uploadSoFar = 0;
            Stream reqStream = req.GetRequestStream();

            for (int i = 0; i < data.Length; i = i + uploadBit)
            {
                int toUpload = Math.Min(uploadBit, data.Length - i);
                uploadSoFar += toUpload;

                reqStream.Write(data, i, toUpload);

                if ((OnUploadProgress != null) && ((j++) % 5 == 0 || uploadSoFar == data.Length))
                {
                    OnUploadProgress(this, new UploadProgressEventArgs(i + toUpload, data.Length, uploadSoFar == data.Length));
                }
            }
            reqStream.Close();
            #endregion
            // let the world make it self out..
            //System.Threading.Thread.Sleep(100); 

            #region Get response
            byte[] receivedDataBuffer = new byte[4096];
            int dataReceived = 0;

            HttpWebResponse response = (HttpWebResponse)req.GetResponse();
            Stream responseStream = response.GetResponseStream();
            
            // loop through response headers
            _responseHeader.Clear();
            for (int i = 0; i < response.Headers.Count; i++)
            {
                _responseHeader.Add(response.Headers.Keys[i], response.Headers[i]);
            }


            int dataRead = responseStream.Read(receivedDataBuffer, 0, receivedDataBuffer.Length);
            dataReceived += dataRead;
            while (dataRead > 0)
            {
                receivingStringBuilder.Append(Encoding.ASCII.GetString(receivedDataBuffer, 0, dataRead));
                dataRead = responseStream.Read(receivedDataBuffer, 0, receivedDataBuffer.Length);
                dataReceived += dataRead;
            }
            responseStream.Close();
            response.Close();

            #endregion

           _response = receivingStringBuilder.ToString();
        }


        public void UploadFile(string localFile, NameValueCollection parameters)
        {
            string boundary = "TASKUBLOGI_MIME_" + DateTime.Now.ToString("yyyyMMddhhmmss");
            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(Url);
            req.Method = "POST";
            req.KeepAlive = false;
            req.Timeout = 50000;
            req.ContentType = "multipart/form-data; boundary=\"" + boundary + "\"";
            req.UserAgent = UserAgent;
            StringBuilder sb = new StringBuilder();


            string[] keys = parameters.AllKeys;
            StringBuilder HashStringBuilder = new StringBuilder();
            Array.Sort(keys);

            foreach (string key in keys)
            {
                HashStringBuilder.Append(key);
                HashStringBuilder.Append(parameters[key]);
                sb.Append("--" + boundary + "\r\n");
                sb.Append("Content-Disposition: form-data; name=\"" + key + "\"\r\n");
                sb.Append("\r\n");
                sb.Append(parameters[key] + "\r\n");
            }

            sb.Append("--" + boundary + "\r\n");
            sb.Append("Content-Disposition: form-data; name=\"sendfile\"; filename=\"" + localFile + "\"\r\n");
            sb.Append("Content-Type: " + HelperUtils.ResolveMimeType(localFile) + "\r\n");
            sb.Append("\r\n");

            UTF8Encoding encoding = new UTF8Encoding();

            byte[] postContents = encoding.GetBytes(sb.ToString());

            FileStream fstream = new FileStream(localFile, FileMode.Open);
            byte[] fileContents = new byte[fstream.Length];
            fstream.Read(fileContents, 0, fileContents.Length);
            fstream.Close();

            byte[] postFooter = encoding.GetBytes("\r\n--" + boundary + "--\r\n");

            byte[] dataBuffer = new byte[postContents.Length + fileContents.Length + postFooter.Length];
            Buffer.BlockCopy(postContents, 0, dataBuffer, 0, postContents.Length);
            Buffer.BlockCopy(fileContents, 0, dataBuffer, postContents.Length, fileContents.Length);
            Buffer.BlockCopy(postFooter, 0, dataBuffer, postContents.Length + fileContents.Length, postFooter.Length);

            req.ContentLength = dataBuffer.Length;

            Stream reqStream = req.GetRequestStream();

            int j = 0;
            int uploadBit = Math.Max(dataBuffer.Length / 100, 50 * 1024);
            int uploadSoFar = 0;

            for (int i = 0; i < dataBuffer.Length; i = i + uploadBit)
            {
                int toUpload = Math.Min(uploadBit, dataBuffer.Length - i);
                uploadSoFar += toUpload;

                reqStream.Write(dataBuffer, i, toUpload);

                //TransferChunk(dataBuffer.Length, i + toUpload);

                if ((OnUploadProgress != null) && ((j++) % 5 == 0 || uploadSoFar == dataBuffer.Length))
                {
                    OnUploadProgress(this, new UploadProgressEventArgs(i + toUpload, (int)req.ContentLength, uploadSoFar == dataBuffer.Length));
                }
            }
            reqStream.Close();

            HttpWebResponse res = (HttpWebResponse)req.GetResponse();
            Stream resStream = res.GetResponseStream();
            resStream.Close();
            res.Close();
        }

        public void DownloadFile(string localFile, string downloadUrl, long size)
        {
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(downloadUrl);
            req.Method = "GET";
            req.UserAgent = "TaskuBlogi 1.0";

            HttpWebResponse resp = (HttpWebResponse)req.GetResponse();

            // Retrieve response stream
            Stream respStream = resp.GetResponseStream();

            // Create local file
            FileStream wrtr = new FileStream(localFile, FileMode.Create);
            try
            {

                // Allocate byte buffer to hold stream contents
                byte[] inData = new byte[4096];

                int transfered = 0;

                int responseLength = (int)resp.ContentLength;

                // loop through response stream reading each data block
                //  and writing to the local file
                int bytesRead = respStream.Read(inData, 0, inData.Length);
                while (bytesRead > 0)
                {
                    transfered += bytesRead;
                    wrtr.Write(inData, 0, bytesRead);
                    bytesRead = respStream.Read(inData, 0, inData.Length);
                    OnDownloadProgress(this,
                        new UploadProgressEventArgs(
                        transfered,
                        responseLength,
                        transfered == responseLength)
                        );
                }
            }
            catch (ThreadAbortException ex)
            {
                Debug.WriteLine(ex.Message);
            }
            finally
            {
                respStream.Close();
                resp.Close();
                wrtr.Close();
            }
        }

        public Bitmap DownloadBitmap()
        {
            return this.DownloadBitmap(Url);
        }

        public Bitmap DownloadBitmap(string downloadUrl)
        {
            Bitmap retBmp = new Bitmap(1, 1);
            Stream respStream = null;
            HttpWebResponse resp = null;
            MemoryStream wrtr = null;
            try
            {
                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(downloadUrl);
                req.Method = "GET";
                resp = (HttpWebResponse)req.GetResponse();

                // Retrieve response stream
                respStream = resp.GetResponseStream();

                // Create local file
                //FileStream wrtr = new FileStream(localFile, FileMode.Create);

                // 64k memorystream
                wrtr = new MemoryStream(64 * 1024);

                try
                {

                    // Allocate byte buffer to hold stream contents
                    byte[] inData = new byte[4096];

                    int transfered = 0;

                    int responseLength = (int)resp.ContentLength;

                    // loop through response stream reading each data block
                    //  and writing to the local file
                    int bytesRead = respStream.Read(inData, 0, inData.Length);
                    while (bytesRead > 0)
                    {
                        transfered += bytesRead;
                        wrtr.Write(inData, 0, bytesRead);
                        bytesRead = respStream.Read(inData, 0, inData.Length);

                        if (OnDownloadProgress != null)
                        {
                            OnDownloadProgress(this,
                                new UploadProgressEventArgs(
                                transfered,
                                responseLength,
                                transfered == responseLength)
                                );
                        }
                    }

                }
                catch (ThreadAbortException)
                {
                }
            }
            catch (WebException)
            {
                Debug.WriteLine("webexception in bitmap download");
            }
            finally
            {
                if (respStream != null)
                    respStream.Close();
                if (resp != null)
                    resp.Close();
                retBmp = new Bitmap(wrtr);
                wrtr.Close();
            }
            return retBmp;
        }
    }

}

